
import { jspack } from 'jspack';


export default class FramePacker {
  public map = {
    CHECK_LINK: {
      cmd: Buffer.from([0x01, 0x00]),
      sendFormat: '>',
      sendFields: [],
      acceptFormat: '>9s',
      acceptFields: [
        'status',
      ],
    },
    RESET_ECS: {
      cmd: Buffer.from([0x01, 0x01]),
      sendFormat: '>',
      sendFields: [],
      acceptFormat: '>B',
      acceptFields: [
        'status',
      ],
    },
    GET_VERSION: {
      cmd: Buffer.from([0x01, 0x02]),
      sendFormat: '>',
      sendFields: [],
      acceptFormat: '>H28s',
      acceptFields: [
        'status',
        'version',
      ],
    },
    TEST_MODEL: {
      cmd: Buffer.from([0x01, 0x03]),
      sendFormat: '>B',
      sendFields: [],
      acceptFormat: '>B28s',
      acceptFields: [
        'status',
        'version',
      ],
    },
    GET_ECS_STATE: {
      cmd: Buffer.from([0x01, 0x04]),
      sendFormat: '>',
      sendFields: [],
      // acceptFormat: '>LLLLLLLLLLLBL',
      // acceptFormat: '<fffffffffffBf',
      acceptFormat: '>lllllllllllBl',
      acceptFields: [
        'voltage1',
        'voltage2',
        'voltage3',
        'voltage4',
        'voltage5',
        'voltage6',
        'electricity1',
        'electricity2',
        'temp1',
        'temp2',
        'cupTemp',
        'charge',
        'dac',
      ],
    },
    GET_MOTOR_STATE: {
      cmd: Buffer.from([0x01, 0x005]),
      sendFormat: '>',
      sendFields: [],
      acceptFormat: '>BBBBBBIIbbBBII',
      acceptFields: [
        'motorARun',
        'motorBRun',
        'motorAControl',
        'motorBControl',
        'motorAPWMDuty',
        'motorBPWMDuty',
        'motorAPWMFrequency',
        'motorBPWMFrequency',
        'motorARate',
        'motorBRate',
        'motorAProtection',
        'motorBProtection',
        'motorACoder',
        'motorBCoder',
      ],
    },
    SET_PROTECTION: {
      cmd: Buffer.from([0x02, 0x00]),
      sendFormat: '>Bl',
      sendFields: ['motor', 'param'],
      acceptFormat: '>B',
      acceptFields: [
        'status',
      ],
    },
    SET_PID: {
      cmd: Buffer.from([0x02, 0x01]),
      sendFormat: '>Blll',
      sendFields: ['motor', 'param1', 'param2', 'param3'],
      acceptFormat: '>B',
      acceptFields: [
        'status',
      ],
    },
    SET_DAC: {
      cmd: Buffer.from([0x02, 0x02]),
      sendFormat: '>Bl',
      sendFields: ['motor', 'param'],
      acceptFormat: '>B',
      acceptFields: [
        'status',
      ],
    },

    SET_SPEED: {
      cmd: Buffer.from([0x02, 0x03]),
      sendFormat: '>bb',
      sendFields: ['motor', 'param'],
      acceptFormat: '>B',
      acceptFields: [
        'status',
      ],
    },
    BEFORE_UPGRADE: {
      cmd: Buffer.from([0x03, 0x00]),
      sendFormat: '>I',
      sendFields: ['motor', 'param1', 'param2', 'param3'],
      acceptFormat: '>B',
      acceptFields: [
        'status',
      ],
    },
    UPGRADE: {
      cmd: Buffer.from([0x03, 0x01]),
      sendFormat: '>Bl',
      sendFields: ['motor', 'param'],
      acceptFormat: '>HIB',
      acceptFields: [
        'status',
        'index',
        'progress'
      ],
    },
  }
  constructor() {
    this.HEADER = 0xEB90;
  }

  /**
   * 计算BCC（异或校验）
   * @param {Uint8Array} bytes - 校验范围字节
   * @returns {number}
   */
  calcBCC(bytes) {
    let bcc = 0;
    for (let i = 0; i < bytes.length; i++) {
      bcc ^= bytes[i];
    }
    return bcc & 0xFF;
  }

  /**
   * 解包函数
   * 用于判断数据帧格式是否合法，并提取消息体
   * @param {Buffer} buffer 输入的完整数据帧
   * @returns {{ valid: boolean, header?: number, cmd?: number, identify?: number, length?: number, data?: Buffer }}
   */
  unpack_old(buffer) {
    try {
      if (buffer.length < 8) return { valid: false, reason: 'too short' };

      const [header, cmd, identify, length] = jspack.Unpack('>HHBH', buffer);
      if (header !== this.HEADER) return { valid: false, reason: 'header error' };

      // if (buffer.length !== 8 + length - 1) {
      //   // 固定长度验证（根据协议可以改为 >=）
      //   // 此处根据协议中倒数1为校验码，总长度应为7 + length + 1
      //   return { valid: false, reason: 'length mismatch' };
      // }

      const data = buffer.slice(7, 7 + length);
      const checksum = buffer[buffer.length - 1];

      const bccRange = buffer.slice(2, buffer.length - 1);
      const calc = this.calcBCC(bccRange);

      if (checksum !== calc) {
        return { valid: false, reason: `checksum error (recv ${checksum}, calc ${calc})` };
      }

      return {
        valid: true,
        header,
        cmd,
        identify,
        length,
        data,
      };
    } catch (err) {
      return { valid: false, reason: err.message };
    }
  }


  mapMessage(message, fields) {
    return Object.fromEntries(fields.map((key, index) => [key, message[index]]))
  }


  unpack(buffer, frame) {
    const message = jspack.Unpack(frame.acceptFormat, buffer)

    return this.mapMessage(message, frame.acceptFields)
  }

  /**
   * 封包函数
   * @param {number} cmd 命令单元
   * @param {number} identify 命令标识
   * @param {Buffer} data 数据单元
   * @returns {Buffer} 完整协议包
   */
  pack(cmd, identify, data) {
    const headerBuf = Buffer.from(jspack.Pack('>H', [this.HEADER]));
    const frame = Buffer.concat([cmd, identify, Buffer.from(jspack.Pack('>H', [data.length])), data]);
    const checksum = this.calcBCC(frame);
    const checksumBuf = Buffer.from([checksum]);
    return Buffer.concat([headerBuf, frame, checksumBuf]);
  }




}

